/**********************************************************************************\
** 文件名称: Uprintf.c
** 创建人员: YCG
** 创建日期: 2018-12-26
** 文档描述: 将数据格式化，通过指定的字符输出函数输出
实例：
//void Uprintf(char *fmt, ...)
//{
//	va_list arp;
//	va_start (arp, fmt); // 将可变长参数转换为va_list 
//  Print(USART_SendChar,fmt,arp);		
//	va_end (arp);	
//}
** 版本代号: V0.1
** 版本说明: 初始版本
\********************************************************************************/
 
#include "print.h"

/*******************************************************************************
** 函数名称: Print
** 功能描述: 打印字符串
** 参数说明: 
SendChar: [输出] 字符输出函数指针，每格式化一个字符，通过这个函数将字符输出
fmt:[输入]       格式化数据，字符串指针
arp:[输入]       可变参数va_list指针
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2018-12-26
********************************************************************************/
void Print(void (*SendChar)(uint8_t), char *fmt, va_list arp)
{
    uint8_t flag, width ;             //标记以及输出数据的宽度
    char s[16];                       //数据处理的临时缓存
    uint8_t i, j;                     //数据处理时的计数
    uint8_t r;                        //保存数据是多少进制
    uint32_t value;                   //用来保存获取的变量值
    char c;                           //c用来保存临时处理的字符
    char dat;                         //dat用来保存临时处理的数据
    char *p;                          //p用来保存字符串的指针位置

    while(1) {
        c = *fmt++;              //获取一个字符进行判断
        if (c == 0) break;			 //到了字符串末尾则 跳出循环
        if (c != '%') {				   //非转义字符
            //      buffer[bufnum++] = c;
            SendChar(c);//将字符放入缓存
            continue;              //结束本次循环
        }
        width = flag = 0;
        c = *fmt++;              //获取%的下一个字符

        if (c == '0') {				   //0：表示填充0，左边的数据全部填充为0
            flag = 1;
            c = *fmt++;  //设置标记0x01，切换到下一个字符
        } else {
            if (c == '-') {			     //-：表示左对齐
                flag = 2;
                c = *fmt++;  //设置标记0x02，切换到下一个字符
            }
        }

        while (c >= '0' && c <= '9') {		//假如是数字的话
            width = width * 10 + c - '0';   //计算宽度
            c = *fmt++;               //切换到下一个字符
        }

        if (c == 'l' || c == 'L') {	//判断是否为长数据
            flag |= 4;
            c = *fmt++;    //设置标记0x04，切换到下一个字符
        }

        if (c == 0) break;        //如果字符串结束了，则退出循环
        dat = c;
        if (dat >= 'a' && dat <= 'z')  dat -= 0x20; //将小写转换为大写
        switch (dat) {				//判断数据类型
        case 'S' :					//字符串型
            p = va_arg(arp, char*);
            for (j = 0; p[j]; j++) ;     //获取字符串的长度
            if (!(flag & 2)) {           //获取标记0x02，非左对齐
                while (j++ < width)  SendChar(' '); //在左边填充小于宽度的空格
            }
            while (*p) SendChar(*p++);           //输出字符串到缓存
            while (j++ < width) SendChar(' ');   //在右边填充小于宽度的空格
            continue;
        case 'C' :					//字符型
            SendChar((char)va_arg(arp, int));
            continue;  //直接将数据放入缓存
        case 'B' :					//二进制
            r = 2;
            break;
        case 'O' :					//八进制
            r = 8;
            break;
        case 'D' :					//带符号十进制
        case 'U' :					//无符号十进制
            r = 10;
            break;
        case 'X' :					//十六进制
            r = 16;
            break;
        default:					  //未知类型
            SendChar(c);
            continue;  //直接输出，进入下一次循环
        }

        //获取一个参数
        value = (dat == 'D') ? va_arg(arp, int32_t) : va_arg(arp, uint32_t);
        if (dat == 'D' && (value & 0x80000000)) {      //判断是否为小数
            value = 0 - value;                         //转换为正数
            flag |= 8;                                 //设置标记0x08，表示负数
        }

        i = 0;
        do {
            dat = (char)(value % r);         //获取最低位
            value /= r;                      //切换到下一位
            if (dat > 9) dat += (c == 'x') ? 0x27 : 0x07;    //大于9的话就是16进制了
            s[i++] = dat + '0';              //将数据转换成为字符
        } while (value && i < sizeof s / sizeof s[0]);   //判断是否转换完成，以及超出转换空间
        if (flag & 8) s[i++] = '-';                      //如果是符输的话加上符号

        j = i;
        dat = (flag & 0x01) ? '0' : ' ';                        //判断标记是填充0还是空格
        while (!(flag & 2) && j++ < width) SendChar(dat); //左边填充
        do {
            SendChar(s[--i]);
        } while (i);
        while (j++ < width)  SendChar(dat);               //右边填充
    }
}
/********************************End of File************************************/
